
 
 
 /* Scroll Bar
 How to use: The getDateRange() returns a int[6] array of the form Start day, Start Month, Start Year, End day, End Month, End Year
 You must give it a start and end range in the same format as above. (Only not as an array)
 And yes, in retrospect I should have REALLY used javas calander class......
 
 */
 
 
  PFont font;
  PImage dateImage,monthImage; //using images for the the date/month numbers around the edge of the box

class Scrollbar extends AWidget {
  int x,y;                //x and y coordinates of small box

  
  
  int barWidth = 200, barHeight =16; //Collapsed bar size
  
  int expandedWidth = 372; //Must be a multiple of 31. Because a gif is used for the number index, please leave this alone
  int expandedHeight = 144; //Multiple of 12 only. Because a gif is used for the number index, please leave this alone
  
  int expandedX = SCREENWIDTH /2 - expandedWidth / 2;  //Full date selector width and height
  int expandedY = SCREENHEIGHT - expandedHeight;
  
  int startDay,endDay,startYear,endYear,startMonth,endMonth;                                           //Inputs that set the range of the scrollBar
  int currentStartDay,currentEndDay,currentStartMonth,currentEndMonth,currentStartYear,currentEndYear; //Values slected by the user, updated when ever the user starts scrolling.

  boolean state = false; //True == expanded, false == collapsed
  color strokeColor = (#e3e3e3); //Stroke color of tab
  
  dayElement[][] arrayofElements = new dayElement[12][31];  //[month][days] Days given a size of 31.
  
  int eventSelector1 =1;
  int eventSelector2 =2;
  
  color startColor = #ffc3a0; //The color of the startign date selector
  color endColor = #a0c8ff;   //The color of the ending date selector
  

  
  daySelector start = new daySelector(expandedX + 10,expandedY + 10,eventSelector1,20,startColor,true);                               //Selector for date start range
  daySelector end = new daySelector(expandedX + expandedWidth - 10,expandedY + expandedHeight -10,eventSelector2,15,endColor,false);  //Selector for endrange
  
  AWidgetListener listener = null;
  
  

  
  
 
  
  public Scrollbar(/*int startDay, int startMonth,int startYear,int endDay, int endMonth, int endYear*/){ //Date Range
    this.x = (SCREENWIDTH / 2) - (barWidth /2);
    this.y = SCREENHEIGHT - barHeight;

    
    /*this.startDay = startDay;
    this.endDay = endDay;
    this.startMonth = startMonth;
    this.endMonth = endMonth;
    this.startYear = startYear;
    this.endYear = endYear;*/
    
     for(int ycol =0; ycol <= 11; ycol++){
        for(int xcol = 0; xcol <=30;xcol++){
          arrayofElements[ycol][xcol] = new dayElement(expandedX  +(expandedWidth  / 31)  * xcol,expandedY + (expandedHeight / 12) * ycol,xcol + 1,ycol +1); //+1 to compensate
          //for the fact that the dates are stored in array


        }
    }
    
     
    /*this.currentStartDay = this.startDay;  //If the user has not selected any values yet, simple assign the start and end of the date range to the output date range
    this.currentEndDay = this.endDay;
    this.currentStartMonth = this.startMonth;
    this.currentEndMonth =  this.endMonth;
    this.currentStartYear = this.startYear;
    this.currentEndYear = this.endYear; */
    
    
    dateImage = loadImage("days.gif");
    monthImage = loadImage("months.gif");  
    
    //Color certain days to indicate they cannot be selected
    //Copy pasta'd code because its easier than making some fancy equation
    arrayofElements[10][30].setElementColor(false,false);
    arrayofElements[8][30].setElementColor(false,false);
    arrayofElements[5][30].setElementColor(false,false);
    arrayofElements[3][30].setElementColor(false,false);
    //Feb, quicker to loop it
    for(int x =28; x <= 30; x++){
    arrayofElements[1][x].setElementColor(false,false);
    }
    setupGrid();  //Draws the grid
    updateGrid(); //Sets the colors of each square that cannot be used
    
    
    

 }
  

  
  void draw(){

    if(state){
      
      if(overRect(expandedX -150,expandedY -150 ,expandedWidth + 300,expandedHeight + 150)){
        expanded();
        if(keyPressed && (key == 'r' || key == 'R')){
          resetRange(); // If the seletors become stuck, simply reset them
          updateGrid();
        }

      }else{
        state = false;
        /* When user mouses off, put method to update date/time here
           Need to have the other method call getDateRange */
        if(listener != null){
          listener.valueChanged(this);
        }           
           
        

      }
    }else{
      collapsed();
      yearRange();
    
    
     

      
    }
  }
  
  
  void expanded(){
    
    /* Draws the complete grid */
    for(int ycol =0; ycol <= 11; ycol++){
        for(int xcol = 0; xcol <=30;xcol++){
          arrayofElements[ycol][xcol].draw();
        }
      }  
      
      //Date and month index images
     image(dateImage, expandedX,expandedY -12);
     image(monthImage, expandedX -11,expandedY - 11);
     image(monthImage, expandedX + expandedWidth,expandedY - 11);
     
     start.draw();  //Start and end date range selectors
     end.draw();
  }
  
 boolean getFocus(){
   return !state;
 }
  
  void collapsed(){
    
    stroke(#555861);
    if(overRect(this.x,this.y,this.barWidth,this.barHeight)){
      strokeColor = color(#303158);
      if(mousePressed){
        state = true;
      }
    }else{
      strokeColor = (#e3e3e3);
    }
    fill(#f2f2f2);
    stroke(strokeColor);
    rect(x,y,barWidth,barHeight);
  }
  
  
  
  class dayElement{       //Each element represents a single date
    int elementHeight = expandedHeight / 12;
    int elementWidth = expandedWidth / 31;
    int elementX;
    int elementY;
    int dateX;
    int monthY;
    color strkColor = #ededed;
    color fillColor = (#e6e6ed);
    color startFill = (#e6e6ed);
    boolean startRange;
    boolean endRange;        





    private dayElement(int x, int y, int dayX,int monthY){
        this.elementX = x;
        this.elementY = y;
        this.dateX = dayX;
        this.monthY = monthY;
        
    }
    
    void draw(){
      
       stroke(strkColor);       
       fill(fillColor);
       
       rect(elementX,elementY,elementWidth,elementHeight);
    }
    
    void setElementColor(boolean start, boolean end){  //Method to set the color of an element.
      startRange = start;                      //An element with the same color as start/day selector indicates that it cannot be used
      endRange = end;                          //An element is a slightly darker gray to indicate no selectors can use it.
      setRange();
      
    }
    
    /* This method sets the color for a single grid element. If the color is the same as a date selector, it means that the date selector cannot be placed on top of the element
    In reality it can, this method simply sets the color to indicate to the user an invalid element. The element still retruns a value as normal, and that value is checked later */
    void setRange(){
      if(!startRange && !endRange){
                fillColor = #dcdcd3;
      }else if(!startRange){
        fillColor = startColor;
      }else if(!endRange){
        fillColor = endColor;
      }else{
        fillColor = startFill; 
      }
      
    }
    
    void setStartRange(boolean input){
      startRange = input;
      setRange();
    }
    
    void setEndRange(boolean input){
      endRange = input;
      setRange();
    }
    
    
    boolean overElement(float xIn,float yIn){
      float x = xIn;
      float y = yIn;
      
      if (x >= elementX && x <= x+elementWidth && 
         y >= elementY && y <= y+elementWidth){
           return true;
         }else{
           return false;
         }
         
    }
   }
     
      
   

   

  
  
  
  
  

  boolean overRect(float x, float y, int width, int height) {
    if (mouseX >= x && mouseX <= x+width && 
         mouseY >= y && mouseY <= y+height) {
      return true;
    } else {
      return false;
    }
  }
  
  
  
  class daySelector{ //The little dots that select the dates. bottom is true if the line go to the bottom of the date selector
    float x,y,mX,mY;
    int event, rectSize;
    color rectColor;
    boolean bottom; //If top is true then the line from the point to the x-axis will always have its y2 = 0
    float mx; //Constrained MouseX
    float my; //Constrained MouseY
    float axisX1;
    float axisY1; 
    float axisX2;
    float axisY2;
    color strokeColor;
    boolean overRect = false;
    //Values for year selector code
    float yearRectWidth;// = expandedWidth/2;
    int yearRectHeight = 15;
    color yearStroke = (#ffffff);
    float oneYear;

     
  
    
     private daySelector(float x,float y,int event, int sizeOfrect,color rectColor,boolean bottom){
        this.x = x;
        this.y =y;
        this.event = event;
        this.rectSize = sizeOfrect;
        this.rectColor = rectColor;
        this.bottom = bottom; //Bottom = true if start of range selector
        if(bottom){           //Set the constraint on the line. No need to put this in draw, but it wont work anywhere else
          this.axisY1 = expandedY;
          this.axisX2 = expandedX;
        }else{
          this.axisY1 = expandedY;
          this.axisX2 = expandedX + expandedWidth;
        }
        this.yearRectWidth = expandedWidth/2;
        

        

      }
     
      void draw(){
        
        if(overRect(x,y,rectSize,rectSize)){
           overRect = true;
          }
        if( mousePressed && mouseButton == LEFT && overRect){
          update();
        }else if(overRect && mousePressed && mouseButton == RIGHT){
          updateYear();
          yearStroke = rectColor;
        }else{         
          overRect = false;
          yearStroke = 255;
        }
          //<grid selectors>

          stroke(0);
          line(x,y,x,axisY1);
          line(x,y,axisX2,y);
          fill(#e0e0e0,170);
          smooth();
          strokeWeight(2);
          stroke(rectColor);
          rectMode(CENTER);
          rect(x,y,rectSize,rectSize);
          point(x -1,y -1);
          strokeWeight(1);
          rectMode(CORNER);
          //</Grid Selectors>
          //<Year Selector>     Also includes code to print the selected values to the screen
          strokeWeight(2);
          stroke(yearStroke);
          if(bottom){
            /* erm... quick hack
            Putting updateCurrentValues here. No point having both slectors call it and it is needed to update the values shown to the user */
            updateCurrentValues(); 
            fill(rectColor);
            rect(expandedX,expandedY  - 12 - yearRectHeight,yearRectWidth,yearRectHeight);  //************12 is the height of the number line gif around the scrollbar**************
            fill(255);
            text(currentStartDay + "/" + currentStartMonth + "/" + currentStartYear,expandedX + expandedWidth/4 - 72/2 ,expandedY -12 -3); //Rough calculation, length of string = 72
          }
          if(!bottom){
             fill(rectColor);
            rect(expandedX + expandedWidth,expandedY  - 12 - yearRectHeight,-yearRectWidth,yearRectHeight);
            fill(255); 
            text(currentEndDay + "/" + currentEndMonth + "/" + currentEndYear,expandedX + expandedWidth - expandedWidth/4 - 72/2,expandedY -12 -3);
            
          }
          strokeWeight(1); 
      }
      
      
      
      void update(){
        mx = constrain(mouseX,expandedX +2,expandedX + expandedWidth -2);    /* Constrains for mouse x and y, sets where the lines comming from the dots should go */
        my = constrain(mouseY,expandedY + 2,expandedY + expandedHeight -2);   
        x = mx;
        y = my; 
        
      }
      
      void updateYear(){
        if(yearRange() != 0){
           yearRectWidth = constrain(mouseX - 300,1,expandedWidth/2);
           oneYear = (expandedWidth /2) / yearRange();  //Having trouble storing the year range, so i'll just call it each time. Don't shoot me
           for(int i = 0; i <= yearRange(); i++){
             if(yearRectWidth <= oneYear * i + 1 && yearRectWidth >= oneYear * i){
               if(bottom){
                 currentStartYear = startYear + i;
               }else{
                 currentEndYear = startYear + i;
               }
             }
           } 
        }
        
        updateGrid();
       
       /* Following two if's account for leap years */
        if(leapYear(currentStartYear)){
          arrayofElements[1][28].setStartRange(true);  
        }
        if(leapYear(currentEndYear)){
          arrayofElements[1][28].setEndRange(true);
        }
      }
      
      
      
     
  }
  
  void updateCurrentValues(){  //Updates the current values selected by the user so that they can be presented to other classes
  
    /*<Section One>
    Determines where the date selectors are positioned on the grid and sets the currents date and month values accordingly */
    
    //Code for updating user selected end element
     for(int ycol = 0; ycol <= 11; ycol++){
        for(int xcol = 0; xcol <=30;xcol++){
          if(arrayofElements[ycol][xcol].overElement(start.x,start.y)){
            currentStartDay = arrayofElements[ycol][xcol].dateX;
            currentStartMonth = arrayofElements[ycol][xcol].monthY;
            }
        }
      }
   //Code for updating user selected end date
   for(int ycol =0; ycol <= 11; ycol++){
        for(int xcol = 0; xcol <=30;xcol++){
          if(arrayofElements[ycol][xcol].overElement(end.x,end.y)){
            currentEndDay = arrayofElements[ycol][xcol].dateX; 
            currentEndMonth = arrayofElements[ycol][xcol].monthY;
          }
        }
      }
      
   //</Section One>
   
   //<Error checking> Make sure it outputs valid dates. Doing this on the backend, the user can still mouse over invalid dates, but they will not be passed outside the object
   //REALLY should have used that calander class.....
      
      if(currentStartYear == startYear){
        if(currentStartMonth == startMonth && currentStartDay >= startDay){
             currentStartMonth = startMonth;
          }else if(currentStartMonth <= startMonth){
             currentStartMonth = startMonth;
             currentStartDay = startDay;            
         }
       
     }
  
    if(currentEndYear == endYear){
        if(currentEndMonth == endMonth && currentEndDay <= endDay){
             currentStartMonth = startMonth;
          }else if(currentEndMonth >= endMonth){
             currentEndMonth = endMonth;
             currentEndDay = endDay;            
         }
       
     }
  
  
  /* Thirty days have september, april, june... blah blah blah */
  if (( currentStartMonth == 4 || currentStartMonth == 6 || currentStartMonth == 9 || currentStartMonth == 11)){
    if(currentStartDay ==  31){   
      currentStartDay = 30;
    }
  }
  if(currentStartMonth == 2 && currentStartDay >29){
      currentStartDay = 28;
   }
   
  if(!leapYear(currentStartYear)){
        if(currentStartMonth == 2){
          if(currentStartDay == 29){
            currentStartDay = 28;
          }
        }
      }
   
  
  if (( currentEndMonth == 4 || currentEndMonth == 6 || currentEndMonth == 9 || currentEndMonth == 11)){
    if(currentEndDay ==  31){   
      currentEndDay = 30;
    }
  }
  if(currentEndMonth == 2 && currentEndDay >29){
      currentEndDay = 28;
   }
   
  
 
        
   if(!leapYear(currentEndYear)){
        if(currentEndMonth == 2){
          if(currentEndDay == 29){
            currentEndDay = 28;
          }
        }
      }
      
      
     
  }
  
  int  yearRange(){   //Returns the range of year to deal with. Many other ways to do this.
  return (endYear - startYear);
  }
  
  
  
  
  void resetRange(){ //Just resets the selectors by dumping in new values to there x and y positions :)
      start.x = expandedX + 10;
      start.y = expandedY + 10;
      end.x = expandedX + expandedWidth - 10; 
      end.y = expandedY + expandedHeight - 10; 
      
  }
  
  boolean leapYear(int inputYear){
  float x = inputYear;
  boolean output =  false;
  if(x == 2000){    //Lazy way of accounting for 2000, the below steps work in every other case.
    output = true;
  }

  x = x /4;

  if((x % 1 == 0)){
    if(x % 100 == 0){
      if(x % 400 == 0){
       output = true;
      }
    }else output= true;        
  }
  return output;

 } 
 
 void setStartGridRange(){  /*Method to set the grid for various ranges. ie if the data start at 1/05/00 then months 1 through 4 should not be selectable for year 2000 */
                             
     for(int y =0; y <= startDay -2; y++){
       arrayofElements[startMonth -1][y].setStartRange(false);
     }
     for(int y = 0; y <= startMonth -2; y++){
       for(int x = 0; x <= 30; x++){
       arrayofElements[y][x].setStartRange(false);
       }
     }
   
 }
 
 void setEndGridRange(){  //As per above method but for the end range
    for(int y = endDay; y <= 30; y++){
       arrayofElements[endMonth][y].setEndRange(false);
     }
     for(int y = endMonth; y <= 11; y++){
       for(int x = 0; x <= 30; x++){
       arrayofElements[y][x].setEndRange(false);
       }
     }
   
 }
 
 /* Method just calls setup grid. However, if the input start/end year is selcted, it calls the relevent methods to indicate to the user which elements cannot be selected */
 void updateGrid(){
    if(currentStartYear == startYear && currentEndYear == endYear){
          setStartGridRange();
          setEndGridRange();
        }else if(currentStartYear == startYear){
          setupGrid();
          setStartGridRange();
        }else if(currentEndYear == endYear){
          setupGrid();
          setEndGridRange();
        }else{
          setupGrid();
        }
 }
 
 void setupGrid(){
   for(int months = 0; months <= 11; months++){
        for(int days = 0; days <= 30; days++){
          arrayofElements[months][days].setElementColor(true,true);  
        }
   }
   //Color certain days to indicate they cannot be selected
    //Copy pasta'd code because its easier than making some fancy equation
    arrayofElements[10][30].setElementColor(false,false);
    arrayofElements[8][30].setElementColor(false,false);
    arrayofElements[5][30].setElementColor(false,false);
    arrayofElements[3][30].setElementColor(false,false);
    //Feb, quicker to loop it
    for(int x =28; x <= 30; x++){
    arrayofElements[1][x].setElementColor(false,false);
    }
    
 }
 
 void setListener(AWidgetListener listener){
    this.listener = listener;
  }
  
  /* Allows other methods to get the dates selected by the user
  Output array of the form( Start day, Start Month, Start Year, End day, End Month, End Year */
  public DateRange getDateRange(){ 
    
    updateCurrentValues(); 
    Date from = new Date(currentStartYear-1900,currentStartMonth -1,currentStartDay);
    Date to = new Date(currentEndYear-1900,currentEndMonth -1,currentEndDay);
   
    DateRange range = new DateRange(from, to);
    return range;
  }
  
  void setDateRange(DateRange range){
    
    Date start = range.getStart();
    Date end = range.getEnd();
    
    this.startDay = start.getDate();
    this.startMonth = start.getMonth() +1;
    this.startYear = start.getYear() + 1900;
    this.endDay = end.getDate();
    this.endMonth = end.getMonth() +1;
    this.endYear = end.getYear() + 1900;
    
    println(dataBase.getMinDate().getYear() +1900);
    
    this.currentStartDay = this.startDay;  //If the user has not selected any values yet, simple assign the start and end of the date range to the output date range
    this.currentEndDay = this.endDay;
    this.currentStartMonth = this.startMonth;
    this.currentEndMonth =  this.endMonth;
    this.currentStartYear = this.startYear;
    this.currentEndYear = this.endYear; 
  }
  
}

